/*
  SOLID - Interference Detection Library
  Copyright (C) 1997 Gino van den Bergen

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

  Please send remarks, questions and bug reports to gino@win.tue.nl,
  or write to:
                  Gino van den Bergen
		  Department of Mathematics and Computing Science
		  Eindhoven University of Technology
		  P.O. Box 513, 5600 MB Eindhoven, The Netherlands
*/

#ifndef _DETECTOR_H_
#define _DETECTOR_H_

#include <SOLID/solid.h>

#include "Shape.h"
#include "Object.h"
#include "Polygon.h"
#include "Response.h"
#include "RespTable.h"
#include "PointBuf.h"

#include <list.h>
#include <map.h>
#include <set.h>

#include <string.h>

typedef list<const Shape *> ShapeList;
typedef map<DtObjectRef, Object *, less<DtObjectRef> > ObjectList;
typedef pair<const Object *, const Object *> ObjectPair;
typedef set<ObjectPair, less<ObjectPair> > ProxList;

class Endpoint;

class Detector {
public:
  static void newShape(const char *name) {
    if (!currentShape) currentShape = new Shape(name);
  }

  static void endShape() {
    if (currentShape) {
      currentShape->finish();
      shapeList.push_back(currentShape);
      currentShape = 0;
    }
  }

  static void deleteShape(const char *name) {
    ShapeList::iterator i = shapeList.begin();
    while (i != shapeList.end() && strncmp((*i)->name, name, 31) != 0) ++i;
    if (i != shapeList.end()) {
      delete *i;
      shapeList.erase(i);
    }
  }
  
  static void vertex(DtScalar x, DtScalar y, DtScalar z) {
    pointBuf.push_back(Point(x, y, z));
  }

  static void newPolygon() {}

  static void endPolygon() {
    if (currentShape) {
      currentShape->push_back(new Polygon(pointBuf.size(), pointBuf));
      pointBuf.clear();
    }
  }


// Object instantiation

  static void newObject(DtObjectRef obj, const char *name) {
    ShapeList::iterator i = shapeList.begin();
    while (i != shapeList.end() && strncmp((*i)->name, name, 31) != 0) ++i;
    if (i != shapeList.end()) {
      move();
      currentObject = objectList[obj] = new Object(obj, *i);
    }
  }

  static void selectObject(DtObjectRef obj) {
    ObjectList::iterator i = objectList.find(obj);
    if (i != objectList.end()) {
      move();
      currentObject = (*i).second;
    }
  }
  
  static void deleteObject(DtObjectRef obj) {
    ObjectList::iterator i = objectList.find(obj);
    if (i != objectList.end()) {
      if (currentObject == (*i).second) currentObject = 0;
      delete (*i).second;
      objectList.erase(i);
    }
  }
  
  static void position(DtScalar x, DtScalar y, DtScalar z) {
    if (currentObject) currentObject->setPosition(Point(x, y, z));
  }
  
  static void orientation(DtScalar x, DtScalar y, DtScalar z, DtScalar w) {
    if (currentObject) currentObject->setOrientation(Quaternion(x, y, z, w));
  }

  static void openGLMatrix(const float *m) { 
    if (currentObject) currentObject->setMatrix(m);
  }
  
  static void openGLMatrix(const double *m) { 
    if (currentObject) currentObject->setMatrix(m);
  }
  
  // Response
  
  static void setDefaultResponse(DtResponse response, DtResponseType type,
			  DtPointer client_data) {
    respTable.setDefault(Response(response, type, client_data));
  }
  
  static void clearDefaultResponse() {
    respTable.setDefault(Response());
  }
  
  static void setObjectResponse(DtObjectRef obj, DtResponse response, 
			 DtResponseType type, DtPointer client_data) {
    respTable.setSingle(obj, Response(response, type, client_data));
  }
  
  static void clearObjectResponse(DtObjectRef obj) {
    respTable.setSingle(obj, Response());
  }
  
  static void resetObjectResponse(DtObjectRef obj) {
    respTable.resetSingle(obj);
  }
  
  static void setPairResponse(DtObjectRef obj1, DtObjectRef obj2, 
		       DtResponse response, DtResponseType type, 
		       DtPointer client_data) {
    respTable.setPair(obj1, obj2, Response(response, type, client_data));
  }
  
  static void clearPairResponse(DtObjectRef obj1, DtObjectRef obj2) {
    respTable.setPair(obj1, obj2, Response());
  }
  
  static void resetPairResponse(DtObjectRef obj1, DtObjectRef obj2) {
    respTable.resetPair(obj1, obj2);
  }
  
  // Runtime
  
  
  static int test();
  
  static void enableNBody() {
    for (ObjectList::const_iterator i = objectList.begin();
	 i != objectList.end(); ++i)
      (*i).second->move();
    nbody = true;
  }
  
  static void disableNBody() { nbody = false; }
  
  static void fixedSizeBBox() { 
    if (currentObject) currentObject->setFixedSize();
  }

  static void dynamicBBox()  { 
    if (currentObject) currentObject->setDynamic(); 
  }

private:
  static ShapeList shapeList;
  static ObjectList objectList;
  static RespTable respTable;
  static ProxList proxList;

  static Shape *currentShape;
  static Object *currentObject;
  static PointBuf pointBuf;
  static PointBuf report;

  static bool nbody;

  static bool object_test(const Object& obj1, const Object& obj2);
  static void move() { 
    if (nbody && currentObject) currentObject->move(); 
  }

  friend void update(const Endpoint& a, const Endpoint& b);
};

#endif
